#ifndef __SYLAR_LOG_H__
#define __SYLAR_LOG_H__

#include <string>
#include <fstream>
#include <sstream>
#include <stdint.h>
#include <memory>
#include <stdarg.h>
#include <vector>
#include <map>
#include <yaml-cpp/yaml.h>
#include "singleton.h"
#include "util.h"
#include "thread.h"


/* #define SYLAR_LOG_LEVEL(logger, level)  \
        if (logger->GetLogLevel() <= level)    \
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level,\
        __FILE__, __LINE__, 0, sylar::GetTheadId(), sylar::GetFiberId(), time(0), "123"))).getSS()
*/

#define SYLAR_LOG_LEVEL(logger, level) \
    if(logger->GetLogLevel() <= level) \
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
                        __FILE__, __LINE__, 0, sylar::GetThreadId(),\
                sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getSS()

#define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::DEBUG)
#define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::INFO)
#define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::ERROR)
#define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::WARN)
#define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::FATAL)


#define SYLAR_LOG_FMT_LEVEL(logger, level, fmt, ...)    \
    if (logger->GetLogLevel() <= level)\
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level,\
        __FILE__, __LINE__, 0, sylar::GetThreadId(), \
        sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getEvent()->setFormat(fmt, __VA_ARGS__);

#define SYLAR_LOG_FMT_DEBUG(logger, fmt, ...)   SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VAR_ARGS__);


#define SYLAR_LOG_FMT_INFO(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)


#define SYLAR_LOG_FMT_WARN(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)


#define SYLAR_LOG_FMT_ERROR(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)


#define SYLAR_LOG_FMT_FATAL(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)

// 获取主日志器
#define SYLAR_LOG_ROOT()  sylar::LoggerMgr::GetInstance()->getRoot()
//获取name的日志器
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)

namespace sylar
{

class Logger;   // 日志器

class LogLevel
{
public:
    // 日志级别
    enum Level
    {
        UNKNOW = 0,
        DEBUG  = 1,
        INFO   = 2,
        WARN   = 3,
        ERROR  = 4,
        FATAL  = 5
    };

public:
    // 转成string
    static const char* toString(const sylar::LogLevel::Level& logLevel);
    // 转成Level
    static const sylar::LogLevel::Level fromString(const std::string& strString);
};

class LogEvent
{
public:
    typedef std::shared_ptr<LogEvent> ptr;
    // 日志事件:程序猿使用日志时触发
    LogEvent(std::shared_ptr<Logger> pLogger ,LogLevel::Level logLevel, 
        const char* cFileName, uint32_t uiLine, uint64_t ulElapse, uint32_t uiTheadId,
        uint32_t uiFiberId, uint64_t ulTime, const std::string& strTheadName);

public:
    const char* getFileName() const { return m_cFileName; }
    uint32_t getLine() const { return m_uiLine; }
    uint64_t getElapse() const { return m_ulElapse; }
    uint32_t getTheadId() const { return m_uiThreadId; }
    uint32_t getFiberId() const { return m_uiFiberId; }
    uint64_t getTime()  const { return m_ulTime; }

    const std::string& getTheadName() const { return m_strTheadName; }

    // 返回日志内容
    std::string getContent() const { return m_ssLog.str(); }

    LogLevel::Level getLogLevel() const { return m_logLevel; }

    std::stringstream& getSS() { return m_ssLog; }

    std::shared_ptr<Logger> getLogger() const { return m_pLogger; }

    void setFormat(const  char* fmt, ...);
    void setFormat(const char* fmt, va_list al);

private:
    const char* m_cFileName = nullptr;  // 文件名称
    uint32_t m_uiLine = 0;              // 行号
    uint64_t m_ulElapse = 0;            // 开始到现在的时间
    uint32_t m_uiThreadId = 0;          // 线程id
    uint32_t m_uiFiberId = 0;           // 协程id
    uint64_t m_ulTime = 0;              // 时间戳
    std::string m_strTheadName;         // 线程名称
    std::stringstream m_ssLog;          // 日志流
    std::shared_ptr<Logger> m_pLogger;  // 当前日志事件所属的日志器
    LogLevel::Level m_logLevel;         // 日志等级
};

class LogEventWrap
{
    // 日志事件的包装器
public:
    LogEventWrap(LogEvent::ptr pLogEvent);
    ~LogEventWrap();

    LogEvent::ptr getEvent() const { return m_pLogEvent; }

    std::stringstream& getSS();

private:
    LogEvent::ptr m_pLogEvent;      // 日志事件
};

class LogFormatter
{   // 日志格式化
public:
    using LogFormatPtr = std::shared_ptr<LogFormatter>;
    using MutexType = Spinlock;
    /**
     * @brief 构造函数
     * @param[in] pattern 格式模板
     * @details 
     *  %m 消息
     *  %p 日志级别
     *  %r 累计毫秒数
     *  %c 日志名称
     *  %t 线程id
     *  %n 换行
     *  %d 时间
     *  %f 文件名
     *  %l 行号
     *  %T 制表符
     *  %F 协程id
     *  %N 线程名称
     *
     *  默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
     */
    LogFormatter(const std::string& strPattern);

    /**
     * @brief 返回格式化日志文本
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] event 日志事件
     */
    
    std::string Format(std::shared_ptr<Logger> pLogger, LogLevel::Level logLevel, LogEvent::ptr pLogEvent);

    std::ostream& Format(std::ostream& ofs, std::shared_ptr<Logger> pLogger, LogLevel::Level logLevel, LogEvent::ptr pLogEvent);

public:
    class BaseLogFormatItem
    {   // 日志内容项格式化,由其他类继承
    public:
        using LogFormatItemPtr = std::shared_ptr<BaseLogFormatItem>;
        virtual  ~BaseLogFormatItem() {};
        /**
         * @brief 格式化日志到流
         * @param[in, out] os 日志输出流
         * @param[in] logger 日志器
         * @param[in] level 日志等级
         * @param[in] event 日志事件
         */
        virtual void Format(std::ostream& os, std::shared_ptr<Logger> Logger, LogLevel::Level Level, LogEvent::ptr Event) = 0;
    };

    // 返回日志模板
    std::string GetPattern() const { return m_strPattern; }


    //初始化,解析日志模板
    void Init();

    bool IsError() const { return m_isError; }

private:
    // 日志格式 默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
    std::string m_strPattern;  
    std::vector<BaseLogFormatItem::LogFormatItemPtr> m_FormatItems; // 日志格式解析后格式
    bool m_isError;            //是否有错误
};

class BaseLogAppender
{
    // 日志输出器基类
    friend class Logger;
public:
    using LogAppenderPtr = std::shared_ptr<BaseLogAppender>;
    using MutexType = Spinlock;
    ~BaseLogAppender() { }
     /**
     * @brief 写入日志
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] event 日志事件
     */
    virtual void Log(std::shared_ptr<Logger> pLogger, LogLevel::Level logLevel, LogEvent::ptr logEvent) = 0;

    // 将日志输出目标的配置转成YAML String
    virtual std::string ToYAMLString() = 0;

    LogFormatter::LogFormatPtr GetLogFormat();
    
    void SetLogFormat(LogFormatter::LogFormatPtr pLogFormat);


    LogLevel::Level GetLevel() const { return m_LogLevel; }
    void SetLevel(LogLevel::Level logLevel) { m_LogLevel = logLevel; }

protected:
    LogLevel::Level m_LogLevel = LogLevel::Level::DEBUG;            // 日志级别
    bool m_bHasFormat = false;         // 标志是否有输出格式
    MutexType m_mutex;
    LogFormatter::LogFormatPtr m_pLogFormat;
};

// 输出 控制台
class StdOutLogAppender : public BaseLogAppender
{
public:
    typedef std::shared_ptr<StdOutLogAppender> ptr;
    void Log(std::shared_ptr<Logger> pLogger, LogLevel::Level logLevel, LogEvent::ptr pLogEvent) override;

    std::string ToYAMLString() override;
};

class FileOutLoggAppender : public BaseLogAppender
{
public:
    typedef std::shared_ptr<FileOutLoggAppender> ptr;

    FileOutLoggAppender(std::string strFileName);
    
    void Log(std::shared_ptr<Logger> pLogger, LogLevel::Level logLevel, LogEvent::ptr pLogEvent) override;

    std::string ToYAMLString() override;

   // 重新打开日志文件
    bool ReOpen();

private:
    std::string m_strFileName;          // 文件路径
    std::ofstream m_FileStream;         // 文件流
    uint64_t m_ulLastOpenTime;            // 上次重新打开时间
};

class Logger : public std::enable_shared_from_this<Logger>
{
    friend class LoggerManager;
public:
    using LoggerPtr = std::shared_ptr<Logger>;
    using MutexType = Spinlock;
    Logger(const std::string& strName = "root");

    // 写日志
    void Log(LogLevel::Level logLevel, LogEvent::ptr plogEvent);

    void Debug(LogEvent::ptr plogEvent);
    void Info(LogEvent::ptr plogEvent);
    void Error(LogEvent::ptr plogEvent);
    void Warn(LogEvent::ptr plogEvent);
    void Fatal(LogEvent::ptr plogEvent);


    void AddAppender(BaseLogAppender::LogAppenderPtr plogAppender);

    void DelAppender(BaseLogAppender::LogAppenderPtr plogAppender);

    void CleanAppender();


    LogLevel::Level GetLogLevel() const {  return m_logLevel; }
    void SetLogLevel(LogLevel::Level logLevel) { m_logLevel = logLevel; }

    const std::string& GetLogName() const { return m_strLoggerName; }


    void SetFormmat(LogFormatter::LogFormatPtr plogFormmat);
    void SetFormmat(const std::string& strFormmat);

    LogFormatter::LogFormatPtr GetFormmat();


    std::string ToYmalString();

    

private:
    std::string m_strLoggerName;   // 日志名称

    LogLevel::Level m_logLevel; // 日志等级

    MutexType m_mutex;

    std::list<BaseLogAppender::LogAppenderPtr> m_Appenders; // 日志输出目标

    LogFormatter::LogFormatPtr m_pLogFormatter;    //日志格式

    Logger::LoggerPtr m_Root;                       // 主日志器
};


class LoggerManager
{
public:
    using MutexType = Spinlock;
    LoggerManager();

    void Init();

    Logger::LoggerPtr getLogger(const std::string& strName);

    Logger::LoggerPtr getRoot() const { return m_RootLogger; }

    std::string toYamlString();

private:
    MutexType m_mutex;
    std::map<std::string, Logger::LoggerPtr> m_Loggers;
    Logger::LoggerPtr m_RootLogger;
};


// 日志器管理类单例模式
using LoggerMgr = Singleton<LoggerManager>;

}

#endif